package jp.sourceforge.akjrcp.audiotools.operations;

import java.util.ArrayList;
import java.util.List;

import jp.sourceforge.akjrcp.audiotools.WaveFormViewer;
import jp.sourceforge.akjrcp.snack.ESPS;
import jp.sourceforge.akjrcp.snack.PitchExecuter;
import jp.sourceforge.akjrcp.subtitle.StandardSubObject;
import jp.sourceforge.akjrcp.ui.draw2d.WaveFormFigureCanvas.TimeSelection;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.AbstractOperation;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;

public class AutoSelectSubtitleOperation extends AbstractOperation{
	public static Log log=LogFactory.getLog(AutoSelectSubtitleOperation.class);
	WaveFormViewer viewer;
	int mode;
	List<StandardSubObject> srtList=new ArrayList<StandardSubObject>();
	public AutoSelectSubtitleOperation(WaveFormViewer viewer,int mode) {
		super("selectSubtitle");
		this.viewer=viewer;
		this.mode=mode;
	}

	@Override
	public IStatus execute(IProgressMonitor monitor, IAdaptable info)
			throws ExecutionException {

		int ms=viewer.getMinContinueSoundScaleValue();
		double pitchTime=viewer.getPitchTime();
		int minCotinue=(int) (((double)ms/1000)/viewer.getPitchTime());
		log.trace("mincont:"+minCotinue);
		ESPS datas[]=viewer.pitchViewFigure.getEspses();
		int min=viewer.getMinVolumeScaleValue();
		
		TimeSelection select=viewer.waveInfoCanvas.getSelection();
		double offsetTime=PitchExecuter.SKIPPED_TIME;
		
		if(select!=null){
			int startIndex=(int) (select.getStart()/(viewer.getPitchTime()*1000));
			if(startIndex<0){
				log.warn("invalid index:"+startIndex);
				startIndex=0;
			}
			int endIndex=(int) (select.getEnd()/(viewer.getPitchTime()*1000));
			if(endIndex>=datas.length){
				log.warn("invalid index:"+endIndex);
				endIndex=datas.length;
			}
			ESPS tmp[]=new ESPS[endIndex-startIndex];
			for (int i = 0; i < tmp.length; i++) {
				tmp[i]=datas[startIndex+i];
			}
			offsetTime=startIndex*viewer.getPitchTime()+PitchExecuter.SKIPPED_TIME;
			datas=tmp;
		}
		
		boolean modified=false;
		if(mode==WaveFormViewer.AUTO_SUBTITLE_PITCH){
		int f=0;
		boolean find=false;
		for(int i=0;i<datas.length;i++){
			if(!find){
				if(datas[i].isVoicing()){
					f=i;
					find=true;
				}
			}else{
				if(!datas[i].isVoicing() || i==datas.length-1){
				int size=i-f;
			//	if(size>=minCotinue){
					long start=(long) (1000*(pitchTime*(f-1)+offsetTime));
					long end=(long) (1000*(pitchTime*(i)+offsetTime));
					StandardSubObject sub=new StandardSubObject();
					sub.setStartTime(start);
					sub.setEndTime(end);
					srtList.add(sub);
					modified=true;
				//}
				find=false;
				}
			}
		}
		
	

		}else if(mode==WaveFormViewer.AUTO_SUBTITLE_PITCH_PLUS || mode==WaveFormViewer.AUTO_SUBTITLE_PITCH_AND_VOLUME){
			double checkBefore=0.07;//ms
			int checkIndex=(int) (((double)checkBefore)/viewer.getPitchTime());
			log.trace(checkIndex+","+viewer.getPitchTime());
			int f=0;
			int lastFind=0;
			boolean find=false;
			for(int i=0;i<datas.length;i++){
				if(!find){
					if(datas[i].isVoicing()){
						f=i;
						find=true;
					}
				}else{
					if(!datas[i].isVoicing()  || i==datas.length-1){//final always set value
					int lastMeasured=f;
					for(int j=f;j>=0 && j>f-checkIndex&&j>lastFind;j--){
						if(datas[j].getMeasurements()>min){
							lastMeasured=j;
						}
					}
					//expand after
					int maxexpand=1;
					int ex=0;
					for(int j=1;j<=maxexpand;j++){
					if(datas[i+j].getMeasurements()>min){
						ex++;
					}else{
						break;
					}
					}
					i+=ex;//expand more;
					//int size=i-f;
				//	if(size>=minCotinue){
						long start=(long) (1000*(pitchTime*(lastMeasured-1)+offsetTime));
						long end=(long) (1000*(pitchTime*(i)+offsetTime));
						StandardSubObject sub=new StandardSubObject();
						sub.setStartTime(start);
						sub.setEndTime(end);
						srtList.add(sub);
						modified=true;
						lastFind=i;
					//}
					find=false;
					}
				}
			}
			
			//volume add
			if(mode==WaveFormViewer.AUTO_SUBTITLE_PITCH_AND_VOLUME){
				ESPS[] tmp2=new ESPS[datas.length];
				for (int i = 0; i < tmp2.length; i++) {
					tmp2[i]=(ESPS) datas[i].clone();
				}
				
				//clear volume old one
				for(int i=0;i<srtList.size();i++){
					StandardSubObject sub=srtList.get(i);
					int sindex=(int) (((double)sub.getStartTime()/1000-offsetTime)/pitchTime);
					int eindex=(int) (((double)sub.getEndTime()/1000-offsetTime)/pitchTime);
					//System.out.println(sindex+","+eindex);
					for(int j=sindex;j<=eindex+1;j++){//expand 1
						if(j>=0 && j<tmp2.length){
						tmp2[j].setMeasurements(0);
						}
					}
				}
				
				List<StandardSubObject> tmpList=new ArrayList<StandardSubObject>();
				boolean ret=autoSubtitleVoice(tmp2,offsetTime,tmpList,min,minCotinue);
				for(int i=0;i<tmpList.size();i++){
					
					//duration set 50ms

					StandardSubObject tmpSub=tmpList.get(i);
					long diff=(tmpSub.getEndTime()-tmpSub.getStartTime());
					int novoiceDuration=(int) Math.max(minCotinue, diff/2);
					long center=tmpSub.getStartTime()+diff/2;
					tmpSub.setStartTime(center-novoiceDuration/2);
					tmpSub.setEndTime(center+novoiceDuration/2);
					srtList.add(tmpSub);
				}
				if(ret){
					modified=true;
				}
			}
			
		

			}else if(mode==WaveFormViewer.AUTO_SUBTITLE_VOLUME){
			
				boolean ret=autoSubtitleVoice(datas,offsetTime,srtList,min,minCotinue);
				if(ret){
					modified=true;
				}
				}
		
		
		if(modified){
			for(int i=0;i<srtList.size();i++){
				viewer.srtFigure.getStandardSubObjectList().add(srtList.get(i));
			}
			updateSubtitle();
			}
		
		return Status.OK_STATUS;

	}
	
	private boolean autoSubtitleVoice(ESPS[] datas,double offsetTime,List<StandardSubObject> list,int min,int minCotinue){
		int f=0;
		int lastFind=0;
		boolean find=false; //find first one.
		boolean modified=false;
		for(int i=0;i<datas.length;i++){
			if(!find){
				if(datas[i].getMeasurements()>min){
					f=i;
					find=true;
				}
			}else{
				if(datas[i].getMeasurements()<min || i==datas.length-1){//check if last
				int lastMeasured=f;
				int size=i-f;
		if(size>=minCotinue){
					long start=(long) (1000*(viewer.getPitchTime()*(lastMeasured-1)+offsetTime));
					long end=(long) (1000*(viewer.getPitchTime()*(i)+offsetTime));
					StandardSubObject sub=new StandardSubObject();
					sub.setStartTime(start);
					sub.setEndTime(end);
					list.add(sub);
					modified=true;
					lastFind=i;
				}
				find=false;
				}
			}
		}
		
	return modified;
	}
	
	
	private void updateSubtitle(){
		viewer.srtFigure.updateSubtitles();
		viewer.selectionChangedSubtitle();
		viewer.setSubtitleModified(true);
	}

	@Override
	public IStatus redo(IProgressMonitor monitor, IAdaptable info)
			throws ExecutionException {
		for(int i=0;i<srtList.size();i++){
			viewer.srtFigure.getStandardSubObjectList().add(srtList.get(i));
		}
		updateSubtitle();
		return Status.OK_STATUS;

	}

	@Override
	public IStatus undo(IProgressMonitor monitor, IAdaptable info)
			throws ExecutionException {
		for(int i=0;i<srtList.size();i++){
			viewer.srtFigure.getStandardSubObjectList().remove(srtList.get(i));
		}
		updateSubtitle();
		return Status.OK_STATUS;

	}
}
